import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';

import 'package:ffi/ffi.dart';
import 'package:flutter/services.dart';

import 'dart:ffi'; // For FFI
import 'dart:io'; // For Platform.isX

final DynamicLibrary skynetCryptLib = Platform.isAndroid
    ? DynamicLibrary.open('libskynet_crypt.so')
    : DynamicLibrary.process();

class NativeAdd {
  static const MethodChannel _channel = MethodChannel('native_add');

  static Future<String?> get platformVersion async {
    final String? version = await _channel.invokeMethod('getPlatformVersion');
    return version;
  }
}

final Pointer<Utf8> Function() _randomkey = skynetCryptLib
    .lookup<NativeFunction<Pointer<Utf8> Function()>>('randomkey')
    .asFunction();
String randomkey() => _randomkey().toDartString();

final Pointer<Utf8> Function(Pointer<Uint8> x) _dhexchange = skynetCryptLib
    .lookup<NativeFunction<Pointer<Utf8> Function(Pointer<Uint8>)>>(
        "dhexchange")
    .asFunction();

String dhexchange(String s) {
  var bt = base64Decode(s);
  final blob = calloc<Uint8>(bt.length);
  for (int i = 0; i < bt.length; i++) {
    blob[i] = bt[i];
  }
  var ret = _dhexchange(blob).toDartString();
  calloc.free(blob);
  return ret;
}

final Pointer<Utf8> Function(Pointer<Uint8> x, Pointer<Uint8> y) _hmac64 =
    skynetCryptLib
        .lookup<
            NativeFunction<
                Pointer<Utf8> Function(
                    Pointer<Uint8>, Pointer<Uint8>)>>("hmac64")
        .asFunction();

String hmac64(String x, String y) {
  var btX = base64Decode(x);
  var btY = base64Decode(y);

  var blobX = calloc<Uint8>(btX.length);
  for (int i = 0; i < btX.length; i++) {
    blobX[i] = btX[i];
  }

  var blobY = calloc<Uint8>(btY.length);
  for (int i = 0; i < btY.length; i++) {
    blobY[i] = btY[i];
  }

  var ret = _hmac64(blobX, blobY).toDartString();

  calloc.free(blobX);
  calloc.free(blobY);

  return ret;
}

final Pointer<Utf8> Function(
        Pointer<Uint8> x, int szX, Pointer<Uint8> y, int szY) _desencode =
    skynetCryptLib
        .lookup<
            NativeFunction<
                Pointer<Utf8> Function(
                    Pointer<Uint8>, Int32, Pointer<Uint8>, Int32)>>("desencode")
        .asFunction();

String desencode(String x, String y) {
  var btX = base64Decode(x);

  List<int> codeUnits = y.codeUnits;
  Uint8List btY = Uint8List.fromList(codeUnits);

  var blobX = calloc<Uint8>(btX.length);
  for (int i = 0; i < btX.length; i++) {
    blobX[i] = btX[i];
  }

  var blobY = calloc<Uint8>(btY.length);
  for (int i = 0; i < btY.length; i++) {
    blobY[i] = btY[i];
  }

  var ret = _desencode(blobX, btX.length, blobY, btY.length).toDartString();
  calloc.free(blobX);
  calloc.free(blobY);

  return ret;
}

final Pointer<Utf8> Function(Pointer<Uint8> x, Pointer<Uint8> y) _dhsecret =
    skynetCryptLib
        .lookup<
            NativeFunction<
                Pointer<Utf8> Function(
                    Pointer<Uint8>, Pointer<Uint8>)>>("dhsecret")
        .asFunction();

String dhsecret(String x, String y) {
  var btX = base64Decode(x);
  var btY = base64Decode(y);

  var blobX = calloc<Uint8>(btX.length);
  for (int i = 0; i < btX.length; i++) {
    blobX[i] = btX[i];
  }

  var blobY = calloc<Uint8>(btY.length);
  for (int i = 0; i < btY.length; i++) {
    blobY[i] = btY[i];
  }

  var ret = _dhsecret(blobX, blobY).toDartString();

  calloc.free(blobX);
  calloc.free(blobY);

  return ret;
}

final int Function(Pointer<Uint8> x) dhtest = skynetCryptLib
    .lookup<NativeFunction<Int32 Function(Pointer<Uint8>)>>("dhtest")
    .asFunction();
